﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

using System;
using Tesla.Bounding;
using Tesla.Content;
using Tesla.Math;

namespace Tesla.Graphics {
    /// <summary>
    /// Interface that defines a camera. Camera's are set to the renderer before a scene is to be rendered,
    /// and contains the viewport used to render the scene to the screen.
    /// </summary>
    public interface ICamera : ISavable {

        /// <summary>
        /// Gets or sets the name of the camera.
        /// </summary>
        String Name {
            get;
            set;
        }

        /// <summary>
        /// Gets the view matrix of the camera.
        /// </summary>
        Matrix ViewMatrix {
            get;
        }

        /// <summary>
        /// Gets the projection matrix of the camera.
        /// </summary>
        Matrix ProjectionMatrix {
            get;
        }

        /// <summary>
        /// Gets the View x Projection matrix.
        /// </summary>
        Matrix ViewProjectionMatrix {
            get;
        }

        /// <summary>
        /// Gets or sets the viewport associated with the camera.
        /// </summary>
        Viewport Viewport {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the position of the camera in world space.
        /// </summary>
        Vector3 Position {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the direction the camera is facing in (forward vector of the camera's frame).
        /// </summary>
         Vector3 Direction {
            get;
            set;
        }

        /// <summary>
        /// Gets or set the left vector of the camera's frame.
        /// </summary>
        Vector3 Right {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the up vector of the camera's frame.
        /// </summary>
        Vector3 Up {
            get;
            set;
        }

        /// <summary>
        /// Gets the projection mode of the camera. This can be se in the SetProjection methods.
        /// </summary>
        ProjectionMode ProjectionMode {
            get;
        }

        /// <summary>
        /// Sets this camera to match the values of the source camera.
        /// </summary>
        /// <param name="source">Source camera to copy from</param>
        void Set(ICamera source);

        /// <summary>
        /// Set the camera's projection, either an orthographic camera or a perspective camera.
        /// </summary>
        /// <param name="mode">Projection mode, perspective or orthographic</param>
        /// <param name="width">Width of the view volume at the near view plane</param>
        /// <param name="height">Height of the view volume at the near view plane</param>
        /// <param name="near">Distance to the near view plane (or min-Z value if ortho)</param>
        /// <param name="far">Distance to the far view plane (or max-Z value if ortho)</param>
        void SetProjection(ProjectionMode mode, float width, float height, float near, float far);

        /// <summary>
        /// Sets the camera's projection. This will always create a perspective camera.
        /// </summary>
        /// <param name="fieldOfView">Field of view, in degrees</param>
        /// <param name="near">Distance to the near view plane</param>
        /// <param name="far">Distance to the far view plane</param>
        void SetProjection(float fieldOfView, float near, float far);

        /// <summary>
        /// Sets the camera's projection, either an orthographic camera or a perspective camera.
        /// </summary>
        /// <param name="mode">Projection mode, perspective or orthographic</param>
        /// <param name="left">Minimum x value of the view volume at the near view plane</param>
        /// <param name="right">Maximum x value of the view volume at the near view plane</param>
        /// <param name="bottom">Minimum y value of the view volume at the near view plane</param>
        /// <param name="top">Maximum y value of the view volume at the near view plane</param>
        /// <param name="near">Distance to the near view plane (or min-Z value if ortho)</param>
        /// <param name="far">Distance to the far view plane (or max-Z value if ortho)</param>
        void SetProjection(ProjectionMode mode, float left, float right, float bottom, float top, float near, float far);

        /// <summary>
        /// Sets the axes of the camera's frame.
        /// </summary>
        /// <param name="right">Right vector</param>
        /// <param name="up">Up vector</param>
        /// <param name="direction">Forward vector</param>
        void SetAxes(Vector3 right, Vector3 up, Vector3 direction);

        /// <summary>
        /// Sets the axes of the camera's frame from a quaternion rotation.
        /// </summary>
        /// <param name="axes">Quaternion rotation</param>
        void SetAxes(Quaternion axes);

        /// <summary>
        /// Sets the axes of the camera's frame from a matrix rotation.
        /// </summary>
        /// <param name="axes">Matrix rotation</param>
        void SetAxes(Matrix axes);

        /// <summary>
        /// Set's the camera's frame.
        /// </summary>
        /// <param name="position">Camera position</param>
        /// <param name="right">Right vector</param>
        /// <param name="up">Up vector</param>
        /// <param name="direction">Forward vector</param>
        void SetFrame(Vector3 position, Vector3 right, Vector3 up, Vector3 direction);

        /// <summary>
        /// Sets the camera's frame from the Quaternion rotation.
        /// </summary>
        /// <param name="position">Camera position</param>
        /// <param name="axes">Quaterion rotation to get the axes from</param>
        void SetFrame(Vector3 position, Quaternion axes);

        /// <summary>
        /// Set's the camera's frame from the Matrix rotation.
        /// </summary>
        /// <param name="position">Camera position</param>
        /// <param name="axes">Matrix rotation to get the axes from</param>
        void SetFrame(Vector3 position, Matrix axes);

        /// <summary>
        /// Updates the camera's state, e.g. the view matrix and bounding frustum. This
        /// should be called whenever the camera's frame or projection matrix change.
        /// </summary>
        void Update();

        /// <summary>
        /// Makes the camera look at the target.
        /// </summary>
        /// <param name="target">Target vector</param>
        /// <param name="worldUp">The up vector in the world</param>
        void LookAt(Vector3 target, Vector3 worldUp);

        /// <summary>
        /// Fills the provided array with the 8 positions representing the corners of the camera's view frustum. The array
        /// must have the appropiate length (8 corners).
        /// </summary>
        /// <param name="array">Array to fill</param>
        void GetFrustumCorners(Vector3[] array);

        /// <summary>
        /// Returns an array of 8 positions, representing the corners of the camera's view frustum.
        /// </summary>
        /// <returns></returns>
        Vector3[] GetFrustumCorners();

        /// <summary>
        /// Tests if a bounding volume is contained in the camera's frustum. Used to make
        /// cull checks. If the volume is null, it will always return ContainmentType.Inside
        /// </summary>
        /// <param name="volume">Volume to test</param>
        /// <returns>Result of the test</returns>
        ContainmentType Contains(BoundingVolume volume);

        /// <summary>
        /// Gets the world position based on the x,y screen coordinates.
        /// </summary>
        /// <param name="screenPosition">Screen position (x,y) and z being the depth (0 - near, 1 - far planes)</param>
        /// <returns>Constructed world position</returns>
        Vector3 GetWorldCoordinates(Vector3 screenPosition);

        /// <summary>
        /// Gets the world position based on the x,y screen position.
        /// </summary>
        /// <param name="screenPosition">Screen position (x,y) and z being the depth (0 - near, 1 - far planes)</param>
        /// <param name="result">Result to store constructed world position</param>
        void GetWorldCoordinates(ref Vector3 screenPosition, out Vector3 result);

        /// <summary>
        /// Gets the screen position based on the world position.
        /// </summary>
        /// <param name="worldPosition">World position</param>
        /// <returns>Screen position</returns>
        Vector2 GetScreenCoordinates(Vector3 worldPosition);

        /// <summary>
        /// Gets the screen position based on the world position.
        /// </summary>
        /// <param name="worldPosition">World position</param>
        /// <param name="result">Result to store screen position in</param>
        void GetScreenCoordinates(ref Vector3 worldPosition, out Vector2 result);

        /// <summary>
        /// Creates a ray to do picking tests, where the origin is on the near plane. This is the same as using
        /// GetWorldCoordinates for (x,y,0) and (x,y,1) and using the normalized difference as the ray's direction.
        /// </summary>
        /// <param name="screenPos">Screen position (x,y) and z being the depth (0 - near, 1 - far planes)</param>
        /// <returns>Resulting ray</returns>
        Ray CreatePickRay(Vector2 screenPos);

        /// <summary>
        /// Creates a ray to do picking tests, where the origin is on the near plane. This is the same as using
        /// GetWorldCoordinates for (x,y,0) and (x,y,1) and using the normalized difference as the ray's direction.
        /// </summary>
        /// <param name="screenPos">Screen position (x,y) and z being the depth (0 - near, 1 - far planes)</param>
        /// <param name="result">Ray containing the result</param>
        void CreatePickRay(ref Vector2 screenPos, out Ray result);
    }
}
